package drds.plus.parser.abstract_syntax_tree.expression.primary.function;

import drds.plus.parser.abstract_syntax_tree.expression.Expression;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.datetime.CurrentDate;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.datetime.CurrentTime;
import drds.plus.parser.abstract_syntax_tree.expression.primary.function.datetime.Now;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class FunctionManager {

    public static final FunctionManager FUNCTION_MANAGER = new FunctionManager(false);
    private final boolean allowFuncDefChange;
    /**
     * non-reserved word named special parser function
     */
    private final HashMap<String, FunctionType> functionNameToFunctionTypeMap = new HashMap<String, FunctionType>();
    /**
     * non-reserved word named ordinary parser function
     */
    private Map<String, Function> functionNameToFunctionMap = new HashMap<String, Function>();

    public FunctionManager(boolean allowFuncDefChange) {
        this.allowFuncDefChange = allowFuncDefChange;


        functionNameToFunctionTypeMap.put("max", FunctionType.MAX);
        functionNameToFunctionTypeMap.put("min", FunctionType.MIN);
        functionNameToFunctionTypeMap.put("sum", FunctionType.SUM);
        functionNameToFunctionTypeMap.put("count", FunctionType.COUNT);
        functionNameToFunctionTypeMap.put("avg", FunctionType.AVG);
        //
        functionNameToFunctionTypeMap.put("row", FunctionType.ROW);
        //

        functionNameToFunctionMap.put("now", new Now());
        functionNameToFunctionMap.put("current_date", new CurrentDate());
        functionNameToFunctionMap.put("current_time", new CurrentTime());


    }

    /**
     * @param extFuncPrototypeMap funcName -&gt; extFunctionPrototype. funcName MUST
     *                            NOT be the same alias predefined function of MySQL
     *                            5.5
     * @throws IllegalArgumentException
     */
    public synchronized void addExtendFunction(Map<String, Function> extFuncPrototypeMap) {
        if (extFuncPrototypeMap == null || extFuncPrototypeMap.isEmpty()) {
            return;
        }
        if (!allowFuncDefChange) {
            throw new UnsupportedOperationException("function define is not allowed to be changed");
        }

        Map<String, Function> toPut = new HashMap<String, Function>();
        // check extFuncPrototypeMap
        for (Entry<String, Function> en : extFuncPrototypeMap.entrySet()) {
            String funcName = en.getKey();
            if (funcName == null)
                continue;
            String funcNameUp = funcName.toUpperCase();
            if (functionNameToFunctionMap.containsKey(funcNameUp)) {
                throw new IllegalArgumentException("ext-function '" + funcName + "' is MySQL's predefined function!");
            }
            Function func = en.getValue();
            if (func == null) {
                throw new IllegalArgumentException("ext-function '" + funcName + "' is null!");
            }
            toPut.put(funcNameUp, func);
        }

        functionNameToFunctionMap.putAll(toPut);
    }

    /**
     * @return null if
     */
    public Function createFunctionExpression(String funcNameUpcase, List<Expression> arguments) {
        Function prototype = functionNameToFunctionMap.get(funcNameUpcase);
        if (prototype == null)
            return null;
        Function func = prototype.constructFunction(arguments);
        func.init();
        return func;
    }

    public FunctionType getFunctionParsingStrategy(String functionName) {
        FunctionType functionType = functionNameToFunctionTypeMap.get(functionName);
        if (functionType == null) {
            if (functionNameToFunctionMap.containsKey(functionName)) {
                return FunctionType.ORDINARY;
            }
            return FunctionType.DEFAULT;
        }
        return functionType;
    }

}
